package javax.realtime;

public abstract class HighResolutionTime implements Comparable, Cloneable {

	public static final int MILLIS_PER_SECOND = 1000;
	public static final int MICROS_PER_SECOND = 1000 * 1000;
	public static final int NANOS_PER_SECOND = 1000 * 1000 * 1000;
	public static final int MICROS_PER_MILLI = 1000;
	public static final int NANOS_PER_MILLI = 1000 * 1000;
	public static final int NANOS_PER_MICRO = 1000 * 1000;
	
	public long milliseconds;
	public int nanoseconds;
	public Clock clock;
	
	public abstract AbsoluteTime absolute(Clock clock);
	public abstract AbsoluteTime absolute(Clock clock, AbsoluteTime dest);
	public Object clone()
	{
		try
		{
			return super.clone();
		}
		catch(CloneNotSupportedException ex)
		{
			throw new InternalError(ex.toString() + ": clone."+ HighResolutionTime.class);
		}
	}
	public int compareTo(HighResolutionTime time)
	{
		if((time == null) || time.clock != this.clock)
			throw new IllegalArgumentException();
		if(time.getClass() != getClass())
			throw new ClassCastException();
		
		if(milliseconds > time.milliseconds) return 1;
		if(milliseconds < time.milliseconds) return -1;
		if(nanoseconds > time.nanoseconds) return 1;
		if(nanoseconds < time.nanoseconds) return -1;
		return 0;
	}
	public int compareTo(Object object)
	{
		return compareTo((HighResolutionTime)object);
	}
	public boolean equals(HighResolutionTime time)
	{
		return milliseconds == time.milliseconds && nanoseconds == time.nanoseconds && clock == time.clock;	
	}
	public boolean equals(Object object)
	{
		if(object instanceof HighResolutionTime) return equals((HighResolutionTime)object);
		return false;
	}
	public Clock getClock()
	{
		return clock;
	}
	public final long getMilliseconds()
	{
		return milliseconds;
	}
	public final int getNanoseconds()
	{
		return nanoseconds;
	}
	public int hashCode()
	{
		return (int)(milliseconds ^ nanoseconds ^ clock.hashCode());
	}
	public abstract RelativeTime relative(Clock clock);
	public abstract RelativeTime relative(Clock clock, RelativeTime dest);
	public void set(HighResolutionTime time)
	{
		if(time == null) throw new IllegalArgumentException();
		if(time.getClass() != getClass()) throw new ClassCastException();
		milliseconds = time.milliseconds;
		nanoseconds = time.nanoseconds;
		clock = time.clock;
	}
	public void set(long millis)
	{
		set(millis, 0);
	}
	public void set(long millis, int nanos)
	{
		if(!_setnormalised(millis, nanos)) throw new IllegalArgumentException();
	}
	public static void waitForObject(Object target, HighResolutionTime time) throws InterruptedException
	{
		
	}
	
	/*
	final boolean _normalise()
	{
		return _normalise(this.milliseconds, this.nanoseconds);
	}
	final void _set_without_normalise(long millis, int nanos)
	{
		milliseconds = millis;
		nanoseconds = nanos;
	}
	*/
	
	// Methods from OVM v1.4
	protected boolean _setnormalised(long millis, int nanos)
	{
		final long millis_in_nanos = nanos / NANOS_PER_MILLI;
        final int nanosleft = (int) (nanos % NANOS_PER_MILLI);
        if (millis > 0) {
            if (nanos < 0) { // no overflow possible
                milliseconds = millis + millis_in_nanos;
                // ensure same sign
                if (milliseconds > 0 && nanosleft != 0) {
                    milliseconds--;
                    nanoseconds = nanosleft + NANOS_PER_MILLI;
                }
                else {
                    nanoseconds = nanosleft;
                }
            }
            else { // watch for overflow
                long tmp = millis + millis_in_nanos;
                if (tmp <= 0) {
//                     System.out.println("setNormalized failing: " + millis + ", " + nanos);
                    return false;
                }
                milliseconds = tmp;
                nanoseconds = nanosleft;
            }
        }
        else if (millis < 0) {
            if (nanos < 0) { // watch for negative overflow
                long tmp = millis + millis_in_nanos;
                if (tmp >= 0) {
//                     System.out.println("setNormalized failing: " + millis + ", " + nanos);
                    return false;
                }
                milliseconds = tmp;
                nanoseconds = nanosleft;
            }
            else { // no overflow possible
                milliseconds = millis + millis_in_nanos;
                // ensure same sign
                if (milliseconds < 0 && nanosleft != 0) {
                    milliseconds++;
                    nanoseconds = nanosleft - NANOS_PER_MILLI;
                }
                else {
                    nanoseconds = nanosleft;
                }
            }
        }
        else { // millis == 0
            milliseconds = millis_in_nanos;
            nanoseconds = nanosleft;
        }

        /*
        //        System.out.println("setNormalized ok: " + milliseconds + ", " + nanoseconds);
        Assert.check(! ((milliseconds < 0 && nanoseconds > 0) ||
                        (milliseconds > 0 && nanoseconds < 0)) ? Assert.OK :
                     "sign mismatch: millis = " + milliseconds + ", nanos = " + nanoseconds);
        Assert.check( nanoseconds >= -9999999 && nanoseconds <= 999999 ?
                      Assert.OK : "nanoseconds out of range");
        */
        return true;
	}
	public static long addSafe(long arg1, long arg2)
	{
		long sum = arg1 + arg2;
		if((arg1 > 0 && arg2 > 0 && sum <= 0) || (arg1 < 0 && arg2 < 0 && sum >= 0)) throw new ArithmeticException();
		return sum;
	}
	
	public static final boolean _normalise(long millis, int nanos)
	{
		final long millis_in_nanos = nanos / NANOS_PER_MILLI;
        
        if (millis > 0) {
            if (nanos < 0) return true; // no overflow possible
            else { // watch for positive overflow
                long tmp = millis + millis_in_nanos;
                if (tmp <= 0) return false;
            }
        }
        else if (millis < 0) {
            if (nanos < 0) { // watch for negative overflow
                long tmp = millis + millis_in_nanos;
                if (tmp >= 0) return false;
            }
        }
        return true;
	}
}
